package com.zzh.lib.core.utils;

import android.annotation.TargetApi;
import android.app.Activity;
import android.app.RecoverableSecurityException;
import android.content.ClipData;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Intent;
import android.content.IntentSender;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.Log;
import android.webkit.MimeTypeMap;

import com.zzh.lib.core.HLibrary;
import com.zzh.lib.core.listener.HFilterResultCallback;
import com.zzh.lib.core.listener.impl.HFileLoaderCallbacks;
import com.zzh.lib.core.model.HAudioFileModel;
import com.zzh.lib.core.model.HFileModel;
import com.zzh.lib.core.model.HImageFileModel;
import com.zzh.lib.core.model.HVideoFileModel;
import com.zzh.lib.core.model.HVideoThumbModel;
import com.zzh.lib.core.model.ResultModel;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.ViewModelStoreOwner;
import androidx.loader.app.LoaderManager;

/**
 * Created by ZZH on 2020/11/30.
 *
 * @Date: 2020/11/30
 * @Email: zzh_hz@126.com
 * @QQ: 1299234582
 * @Author: zzh
 * @Description: 媒体操作工具。保存图片到相册。
 */
public class HMediaUtils {

    public static final int TYPE_IMAGE = 1;
    public static final int TYPE_AUDIO = 2;
    public static final int TYPE_VIDEO = 3;
    public static final int TYPE_FILE = 0;


    public static final String DOC = "application/msword";
    public static final String DOCX = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
    public static final String XLS = "application/vnd.ms-excel application/x-excel";
    public static final String XLSX = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
    public static final String PPT = "application/vnd.ms-powerpoint";
    public static final String PPTX = "application/vnd.openxmlformats-officedocument.presentationml.presentation";
    public static final String PDF = "application/pdf";

    /**
     * 保存文件
     *
     * @param fileName 图片名称
     * @param bitmap   图片资源
     */
    public static ResultModel savePicture(String fileName, Bitmap bitmap) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            Long mImageTime = System.currentTimeMillis();
            final ContentValues values = new ContentValues();
            values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES); //Environment.DIRECTORY_SCREENSHOTS:截图,图库中显示的文件夹名。"dh"
            values.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName);
            values.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpg");
            values.put(MediaStore.MediaColumns.DATE_ADDED, mImageTime / 1000);
            values.put(MediaStore.MediaColumns.DATE_MODIFIED, mImageTime / 1000);
            values.put(MediaStore.MediaColumns.DATE_EXPIRES, (mImageTime + DateUtils.DAY_IN_MILLIS) / 1000);
            values.put(MediaStore.MediaColumns.IS_PENDING, 1);
            ContentResolver resolver = HLibrary.getInstance().getContext().getContentResolver();
            final Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
            try {
                try (OutputStream out = resolver.openOutputStream(uri)) {
                    if (!bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out)) {
                        throw new IOException("Failed to compress");
                    }
                    out.flush();
                }
                values.clear();
                values.put(MediaStore.MediaColumns.IS_PENDING, 0);
                values.putNull(MediaStore.MediaColumns.DATE_EXPIRES);
                LogUtils.d("-------图片路径：" + uri.toString());
                resolver.update(uri, values, null, null);
                return new ResultModel(true, uri);
            } catch (IOException e) {
                resolver.delete(uri, null);
                LogUtils.e("Exception: " + e.toString());
                return new ResultModel(false);
            }
        } else {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
            String pathName = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getAbsolutePath() + File.separator + fileName;
            boolean file = HFileUtils.saveFile(baos.toByteArray(), pathName);
            return new ResultModel(file, pathName);
        }
    }

    /**
     * 保存文件
     *
     * @param fileName 图片名称
     * @param bitmap   图片字节数组
     */
    public static ResultModel savePicture(String fileName, byte[] bitmap) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            Long mImageTime = System.currentTimeMillis();
            final ContentValues values = new ContentValues();
            values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES); //Environment.DIRECTORY_SCREENSHOTS:截图,图库中显示的文件夹名。"dh"
            values.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName);
            values.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpg");
            values.put(MediaStore.MediaColumns.DATE_ADDED, mImageTime / 1000);
            values.put(MediaStore.MediaColumns.DATE_MODIFIED, mImageTime / 1000);
            values.put(MediaStore.MediaColumns.DATE_EXPIRES, (mImageTime + DateUtils.DAY_IN_MILLIS) / 1000);
            values.put(MediaStore.MediaColumns.IS_PENDING, 1);

            ContentResolver resolver = HLibrary.getInstance().getContext().getContentResolver();
            final Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
            try {
                try (OutputStream out = resolver.openOutputStream(uri)) {
                    out.write(bitmap);
                    out.flush();
                }
                values.clear();
                values.put(MediaStore.MediaColumns.IS_PENDING, 0);
                values.putNull(MediaStore.MediaColumns.DATE_EXPIRES);
                resolver.update(uri, values, null, null);
                return new ResultModel(true, uri);
            } catch (IOException e) {
                resolver.delete(uri, null);
                Log.d("Exception", e.toString());
                return new ResultModel(false);
            }
        } else {
            String pathName = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getAbsolutePath() + File.separator + fileName;
            boolean success = HFileUtils.saveFile(bitmap, pathName);

            return new ResultModel(success, pathName);
        }
    }

    /**
     * 获取文件的绝对路径; android 10 以及以下版本；可以正常使用，但是Android 11，12 等高版本，如果选择的是一个word,txt文件则无法获取文件的绝对路径
     *
     * @param uri uri
     * @return path
     */
    public static String queryAbsolutePath(Uri uri) {
        String filePath = null;
        if (DocumentsContract.isDocumentUri(HLibrary.getInstance().getContext(), uri)) {
            // 如果是document类型的 uri, 则通过document id来进行处理
            String documentId = DocumentsContract.getDocumentId(uri);

            if (!TextUtils.isEmpty(documentId)) {
                if (documentId.startsWith("raw:")) {
                    filePath = documentId.substring(4);
                } else {
                    if (isMediaDocument(uri)) { // MediaProvider
                        // 使用':'分割
                        String[] split = documentId.split(":");
                        String id = split[1];
                        String type = split[0];
                        String selection = MediaStore.Images.Media._ID + "=?";
                        String[] selectionArgs = {id};
                        Uri contentUri = null;
                        if ("image".equals(type)) {
                            contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                        } else if ("video".equals(type)) {
                            contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                        } else if ("audio".equals(type)) {
                            contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                        } else if (TextUtils.equals("document", type)) {
                            contentUri = uri;
                        }

                        LogUtils.e(type + ", " + uri.getAuthority());
                        LogUtils.e(contentUri.toString());
                        LogUtils.e(contentUri.getPath());

                        filePath = getDataColumn(contentUri, selection, selectionArgs);
                    } else if (isDownloadsDocument(uri)) { // DownloadsProvider
                        Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(documentId));
                        filePath = getDataColumn(contentUri, null, null);
                    } else if (isExternalStorageDocument(uri)) {
                        final String[] split = documentId.split(":");
                        final String type = split[0];
                        if ("primary".equalsIgnoreCase(type)) {
                            return Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + split[1];
                        }
                    }
                }
            }

        } else if ("content".equalsIgnoreCase(uri.getScheme())) {
            // 如果是 content 类型的 Uri
            filePath = getDataColumn(uri, null, null);
        } else if ("file".equals(uri.getScheme())) {
            // 如果是 file 类型的 Uri,直接获取图片对应的路径
            filePath = uri.getPath();
        }
        return filePath;
    }

    /**
     * 判断SD卡里面的文件
     *
     * @param uri
     * @return
     */
    private static boolean isExternalStorageDocument(Uri uri) {
        return "com.android.externalstorage.documents".equals(uri.getAuthority());
    }

    /**
     * 获取文件的绝对路径
     *
     * @param uri
     * @param selection
     * @param selectionArgs
     * @return
     */
    private static String getDataColumn(Uri uri, String selection, String[] selectionArgs) {
        String path = null;

        String[] projection = new String[]{MediaStore.Images.Media.DATA};
        Cursor cursor = null;
        try {
            cursor = HLibrary.getInstance().getContext().getContentResolver().query(uri, projection, selection, selectionArgs, null);
            if (cursor != null && cursor.moveToFirst()) {
                int columnIndex = cursor.getColumnIndexOrThrow(projection[0]);
                path = cursor.getString(columnIndex);
            }
        } catch (Exception e) {
            if (cursor != null) {
                cursor.close();
            }
        }
        return path;
    }

    /**
     * @param uri the Uri to check
     * @return Whether the Uri authority is MediaProvider
     */
    private static boolean isMediaDocument(Uri uri) {
        return "com.android.providers.media.documents".equals(uri.getAuthority());
    }

    /**
     * @param uri the Uri to check
     * @return Whether the Uri authority is DownloadsProvider
     */
    private static boolean isDownloadsDocument(Uri uri) {
        return "com.android.providers.downloads.documents".equals(uri.getAuthority());
    }

    /**
     * 选择多张图片。默认单选，长按进入多选模式。
     * 在接收处理时需要注意：
     * 1、单选使用的是Intent.getData()返回的数据，存储的是一张图片的Uri。需要注意这个时候Intent.getClipData返回值是空
     * 2、多选使用的是Intent.getClipData()存储的是一个集合图片Uri。需要注意这个时候Intent.getClipData返回值是空
     *
     * @param ctx         页面
     * @param requestCode 请求码
     */
    public static void startMultiPicture(Activity ctx, int requestCode) {
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);//意图：文件浏览器
        intent.setType("image/*");//限制图片限制
        intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);//多选
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        ctx.startActivityForResult(intent, requestCode);
    }

    /**
     * 获取图片路径
     *
     * @param data 返回的data数据
     */
    public static List<String> obtainMultiPicture(Intent data) {
        List<String> list = new ArrayList<>();

        if (data != null) {
            Uri uri = data.getData();
            if (uri != null) {
                String path = queryAbsolutePath(uri);
                list.add(path);
            }

            ClipData dataClipData = data.getClipData();
            if (dataClipData != null) {
                int itemCount = dataClipData.getItemCount();
                if (itemCount > 0) {
                    for (int i = 0; i < itemCount; i++) {
                        ClipData.Item itemAt = dataClipData.getItemAt(i);
                        if (itemAt.getUri() != null) {
                            list.add(queryAbsolutePath(itemAt.getUri()));
                        }
                    }
                }
            }
        }
        return list;
    }


    /**
     * 单选图片
     *
     * @param ctx         页面
     * @param requestCode 请求码
     */
    public static void startSinglePicture(Activity ctx, int requestCode) {
        Intent intent = new Intent(Intent.ACTION_PICK);//意图：文件浏览器
        intent.setType("image/*");//限制图片限制
        intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false);//多选
        ctx.startActivityForResult(intent, requestCode);
    }

    /**
     * 获取单张图片路径
     *
     * @param data 返回的data数据
     */
    public static String obtainSinglePicture(Intent data) {
        if (data != null) {
            Uri uri = data.getData();
            if (uri != null) {
                return queryAbsolutePath(uri);

            }
        }
        return null;
    }

    /**
     * 多选文档。
     *
     * @param ctx
     * @param requestCode
     */
    public static void startMultiDocuments(Activity ctx, int requestCode) {
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);//意图：文件浏览器
        intent.setType("application/*");
        intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);//多选
        intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[]{DOC, DOCX, PPT, PPTX, PDF, XLS, XLSX});
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        ctx.startActivityForResult(intent, requestCode);
    }

    /**
     * 单选文档。
     *
     * @param ctx
     * @param requestCode 请求code
     */
    public static void startSingleDocuments(Activity ctx, int requestCode) {
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);//意图：文件浏览器
        intent.setType("application/*");
        intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false);//多选
        intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[]{DOC, DOCX, PPT, PPTX, PDF, XLS, XLSX});
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        ctx.startActivityForResult(intent, requestCode);
    }

    /**
     * 获取图片路径
     *
     * @param data 返回的data数据
     */
    public static List<String> obtainMultiDocuments(Intent data) {
        List<String> list = new ArrayList<>();

        if (data != null) {
            Uri uri = data.getData();
            if (uri != null) {
                String path = queryAbsolutePath(uri);
                LogUtils.d(path);
                list.add(path);
            }

            ClipData dataClipData = data.getClipData();
            if (dataClipData != null) {
                int itemCount = dataClipData.getItemCount();
                if (itemCount > 0) {
                    for (int i = 0; i < itemCount; i++) {
                        ClipData.Item itemAt = dataClipData.getItemAt(i);
                        if (itemAt.getUri() != null) {
                            String path = queryAbsolutePath(itemAt.getUri());
                            LogUtils.d(path);
                            list.add(path);
                        }
                    }
                }
            }
        }
        return list;
    }

    public static List<String> obtainSingleDocuments(Intent data) {
        List<String> list = new ArrayList<>();

        if (data != null) {
            Uri uri = data.getData();
            if (uri != null) {
                String path = queryAbsolutePath(uri);
                LogUtils.d(path);
                list.add(path);
            }

            ClipData dataClipData = data.getClipData();
            if (dataClipData != null) {
                int itemCount = dataClipData.getItemCount();
                if (itemCount > 0) {
                    for (int i = 0; i < itemCount; i++) {
                        ClipData.Item itemAt = dataClipData.getItemAt(i);
                        if (itemAt.getUri() != null) {
                            String path = queryAbsolutePath(itemAt.getUri());
                            LogUtils.d(path);
                            list.add(path);
                        }
                    }
                }
            }
        }
        return list;
    }

    public static List<Uri> obtainDocumentsUri(Intent data) {
        List<Uri> list = new ArrayList<>();

        if (data != null) {
            Uri uri = data.getData();
            if (uri != null) {
                list.add(uri);
                LogUtils.d(uri.toString());
            }

            ClipData dataClipData = data.getClipData();
            if (dataClipData != null) {
                int itemCount = dataClipData.getItemCount();
                if (itemCount > 0) {
                    for (int i = 0; i < itemCount; i++) {
                        ClipData.Item itemAt = dataClipData.getItemAt(i);
                        if (itemAt.getUri() != null) {
                            list.add(itemAt.getUri());
                            LogUtils.d(itemAt.getUri().toString());
                        }
                    }
                }
            }
        }
        return list;
    }

    /**
     * 复制文件到项目内部存储，
     *
     * @param fileUri      文件URI
     * @param cacheDirName 临时缓存目录
     */
    public static String copyFileCacheInterval(Uri fileUri, String cacheDirName, String fileName) {
        try {
            InputStream inputStream = HLibrary.getAppContext().getContentResolver().openInputStream(fileUri);
            File destination = new File(HFileUtils.getCacheDir() + File.separator + cacheDirName, fileName);
            if (!destination.getParentFile().exists()) {
                destination.getParentFile().mkdirs();
            }
            destination.deleteOnExit();
            HFileUtils.copyToFile(inputStream, destination);
            return destination.getAbsolutePath();
        } catch (IOException e) {
            LogUtils.e(e);
        }
        return null;
    }


    /**
     * 复制文件到程序内部存储空间
     * ANDROID R 11 以及以上版本
     *
     * @param fileUris     文件uri集合
     * @param cacheDirName 文件内部存储空间位置
     * @return 返回文件绝对路径集合
     */
    @TargetApi(Build.VERSION_CODES.R)
    public static List<String> copyFileCacheInterval(List<Uri> fileUris, String cacheDirName) {
        List<String> list = new ArrayList<>();

        if (fileUris != null && !fileUris.isEmpty()) {
            for (Uri uri : fileUris) {
                String extensionFromMimeType = MimeTypeMap.getSingleton().getExtensionFromMimeType(HLibrary.getAppContext().getContentResolver().getType(fileUris.get(0)));
                String interval = copyFileCacheInterval(uri, cacheDirName, System.nanoTime() + "." + extensionFromMimeType);
                list.add(interval);
            }
        }

        return list;
    }

    /**
     * 删除文件
     *
     * @param fileUri 文件uri
     * @param reqCode 请求码
     */
    public static void deleteFile(Uri fileUri, int reqCode) {
        LogUtils.d("deleteFile_uri:" + fileUri);
        ContentResolver mContentResolver = HLibrary.getAppContext().getContentResolver();
        try {
            // 对于未启用分区存储的情况，若权限申请到位，则以下代码可以执行成功直接删除
            int count = mContentResolver.delete(fileUri, null, null);
            boolean result = (count == 1);
            LogUtils.d("DeleteImage result:" + result);
        } catch (SecurityException exception) {
            // 若启用了分区存储，上面代码delete将会报错，显示没有权限。
            // 需要捕获这个异常，并用下面代码，使用startIntentSenderForResult弹出弹窗向用户请求修改当前图片的权限
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                RecoverableSecurityException recoverableSecurityException;
                if (exception instanceof RecoverableSecurityException) {
                    recoverableSecurityException = (RecoverableSecurityException) exception;
                } else {
                    throw new RuntimeException(exception.getMessage(), exception);
                }
                IntentSender intentSender = recoverableSecurityException.getUserAction().getActionIntent().getIntentSender();
                try {
                    HLibrary.getLastActivity().startIntentSenderForResult(intentSender, reqCode, null, 0, 0, 0, null);
                } catch (IntentSender.SendIntentException e) {
                    e.printStackTrace();
                }
            } else {
                throw new RuntimeException(exception.getMessage(), exception);
            }
        }
    }

    /**
     * 获取所有的视频
     */
    public static <T extends LifecycleOwner & ViewModelStoreOwner> void getAllVideos(T owner, HFilterResultCallback<HVideoFileModel> callback) {
        LoaderManager.getInstance(owner).initLoader(1, null, new HFileLoaderCallbacks(HLibrary.getLastActivity(), callback, TYPE_VIDEO));
    }

    /**
     * 获取所有的文件
     */
    public static <T extends LifecycleOwner & ViewModelStoreOwner> void getAllFiles(T owner, HFilterResultCallback<HFileModel> callback) {
        LoaderManager.getInstance(owner).initLoader(0, null, new HFileLoaderCallbacks(HLibrary.getLastActivity(), callback, TYPE_FILE));
    }

    /**
     * 获取所有的音频
     */
    public static <T extends LifecycleOwner & ViewModelStoreOwner> void getAllAudios(T owner, HFilterResultCallback<HAudioFileModel> callback) {
        LoaderManager instance = LoaderManager.getInstance(owner);
        instance.initLoader(2, null, new HFileLoaderCallbacks(HLibrary.getLastActivity(), callback, TYPE_AUDIO));
    }

    /**
     * 获取所有的图片
     */
    public static <T extends LifecycleOwner & ViewModelStoreOwner> void getAllImages(T owner, HFilterResultCallback<HImageFileModel> callback) {
        LoaderManager.getInstance(owner).initLoader(3, null, new HFileLoaderCallbacks(HLibrary.getLastActivity(), callback, TYPE_IMAGE));
    }

    public static HVideoThumbModel getVideoAllThumbs(String path, int frame, Handler videoHandler) {
        if (TextUtils.isEmpty(path) || frame <= 0) {
            LogUtils.w("path 为空, 或者frame <=0");
            return null;
        }
        final MediaMetadataRetriever mediaMetadata = new MediaMetadataRetriever();
        mediaMetadata.setDataSource(path);
        HVideoThumbModel model = new HVideoThumbModel();
        String mVideoRotation = mediaMetadata.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
        int mVideoWidth = Integer.parseInt(mediaMetadata.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH));
        int mVideoHeight = Integer.parseInt(mediaMetadata.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT));
        int mVideoDuration = Integer.parseInt(mediaMetadata.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION));
        model.setVideoHeight(mVideoHeight);
        model.setVideoWidth(mVideoWidth);
        model.setVideoDuration(mVideoDuration);
        model.setVideoRotation(mVideoRotation);

        HTaskUtils.getDefaultInstance().execute(() -> {
            final int frameTime = mVideoDuration / frame * 1000;
            for (int x = 0; x < frame; x++) {
                Bitmap bitmap = mediaMetadata.getFrameAtTime(frameTime * x, MediaMetadataRetriever.OPTION_CLOSEST_SYNC);
                Message msg = videoHandler.obtainMessage();
                msg.what = 1;
                msg.obj = bitmap;
                msg.arg1 = x;
                videoHandler.sendMessage(msg);
            }
            try {
                mediaMetadata.release();
            } catch (IOException e) {
                LogUtils.e(e);
            }
        });
        return model;
    }


}
